﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PSIEpics;
using System.Collections;
using string_short;
using jtext_EpicsClient;
using System.Text.RegularExpressions;
using System.Threading;

namespace jtext_EpicsClient
{
    delegate int GetValHandler(ref object Val, EpicsChannel tChannel);
    delegate int SetValHandler(object Val, EpicsChannel tChannel);        
    
    /// <summary>
    /// with this you can access pv with out PSIEpics channel
    /// </summary>
    public class RecordValue
    {
        JECLient myClient;
        GetValHandler getValDelegate;
        SetValHandler setValDelegate;
        
        /// <summary>
        /// Initializes a new instance of the <see cref="RecordValue"/> class.
        /// <para>automaticly done by the JECLient</para>
        /// </summary>
        /// <param name="Client">The EJclient.</param>
        public RecordValue(JECLient Client)
        {
            myClient = Client;
            getValDelegate = new GetValHandler(tryGetVal);
            setValDelegate = new SetValHandler(trySetVal);
        }

        /// <summary>
        /// Gets or sets the <see cref="System.Object"/> with the specified pv name.
        /// <para>you can only set numberic values that can be convert into double</para>
        /// <para>NO!!New features you can set int  double and int[] double[] and sbyte[] directely</para>
        /// <para>NOTICE!! if the pv is not ready, you get a "connectin error" exception, if the pv is ready then it's gone you get a "connection time out error"
        /// And you get no error after the pv is back online</para>
        /// </summary>
        public object this[string PvName]
        {
            get
            {
                EpicsChannel TChannel = myClient[PvName];   //get the channel value you want
                return getVal(TChannel);
                
                /*
                //if (TChannel.Status != ChannelStatus.CONNECTED)
                //{
                //    throw new SystemException("connection error.");
                //}
                //try
                //{
                //    object rTn = TChannel.Get();
                //    return rTn;
                //}
                //catch
                //{
                //    throw new SystemException("connection error.");
                //}
                 */

            }
            set
            {
                EpicsChannel TChannel = myClient[PvName];
                setVal(value, TChannel);
                /*
                //if (TChannel.Status != ChannelStatus.CONNECTED)
                //{
                //    throw new SystemException("connection error.");
                //}
                //try
                //{
                //    try
                //    {
                //        TChannel.Put((double)value);        //you can only set numberic values that can be convert into double      
                //    }
                //    catch
                //    {
                //        TChannel.Put((int)value);
                //    }
                //    finally
                //    {
                //        //nothing
                //    }
                //}
                //catch
                //{
                //    //try to convert
                //    sbyte[] sb = value as sbyte[];
                //    int[] Tint = value as int[];
                //    double[] Tdoub = value as double[];

                //    if (sb != null)
                //    {
                //        TChannel.Put(sb);
                //    }
                //    else if (Tint != null)
                //    {
                //        TChannel.Put(Tint);
                //    }
                //    else if (Tdoub != null)
                //    {
                //        TChannel.Put(Tdoub);
                //    }
                //    else
                //    {
                //        throw new SystemException("type not supported or connection error.");
                //    }
                //}
                 * */
            }

        }

        /// <summary>
        /// Gets the string frome a pv. Make sure the pv is used as string.
        /// </summary>
        /// <param name="PvName">Name of the pv.</param>
        /// <returns></returns>
        public string GetString(string PvName)
        {
            EpicsChannel TChannel = myClient[PvName];    //get the channel value you want
            sbyte[] msg=(sbyte[])getVal(TChannel);
            string myStr = Str_Short.SByteToString(msg);
            string[] myStrs = Regex.Split(myStr, "\0");
            return myStrs[0];
            /*
            if (TChannel.Status != ChannelStatus.CONNECTED)
            {
                throw new SystemException("connection error.");
            }
            try
            {
                sbyte[] msg = TChannel.Get<sbyte[]>();
                string myStr = Str_Short.SByteToString(msg);
                string[] myStrs = Regex.Split(myStr, "\0");
                return myStrs[0];
            }
            catch
            {
                throw new SystemException("type not supported or connection error.");
                //return "";
            }*/

        }

        /// <summary>
        /// Sets the string. Make sure the pv is used as string.
        /// </summary>
        /// <param name="PvName">Name of the pv.</param>
        /// <param name="Message">The message.</param>
        /// <returns></returns>
        public int SetString(string PvName, string Message)
        {
            //return  the lenth of the short 
            EpicsChannel TChannel = myClient[PvName];  //get the channel value you want
            sbyte[] msg = Str_Short.StringToSByte(Message);
            setVal(msg,TChannel);
            return msg.Length;
            /*
             * if (TChannel.Status != ChannelStatus.CONNECTED)
            {
                throw new SystemException("connection error.");
            }
            try
            {
                sbyte[] msg = Str_Short.StringToSByte(Message);
                TChannel.Put(msg);
                return msg.Length;
            }
            catch
            {
                throw new SystemException("type not supported or connection error.");
                //return -1;
            }*/

        }

        /// <summary>
        /// Tries the get val, if time out throw exception
        /// </summary>
        /// <param name="Val">The val.</param>
        /// <param name="tChannel">The t channel.</param>
        /// <returns></returns>
        private int tryGetVal(ref object Val, EpicsChannel tChannel)
        {
            //EpicsChannel TChannel = myClient[PvName];   //get the channel value you want
            if (tChannel.Status != ChannelStatus.CONNECTED)
            {
                throw new SystemException("connection error.");
            }
            else
            {
                try
                {
                    Val = tChannel.Get();
                    return 0;
                }
                catch
                {
                    throw new SystemException("connection time out.");
                }
            }
            //return 0;
        }


        private Object getVal(EpicsChannel tChannel)
        {
            object Val=new object();
            IAsyncResult ASResult =getValDelegate.BeginInvoke(ref Val,tChannel, null, null);
            if (ASResult.AsyncWaitHandle.WaitOne(3000, false) == true)
            {
                getValDelegate.EndInvoke(ref Val,ASResult);
                return Val;
            }
            else
            {
                throw new SystemException("connection time out.");
            }
            //return null;
        }


        private int trySetVal(object Val, EpicsChannel tChannel)
        {
            if (tChannel.Status != ChannelStatus.CONNECTED)
            {
                throw new SystemException("connection error.");
            }
            else
            {
                try
                {
                    try
                    {
                        tChannel.Put((double)Val);        //you can only set numberic values that can be convert into double      
                    }
                    catch
                    {
                        tChannel.Put((int)Val);
                    }
                    finally
                    {
                        //nothing
                    }
                    return 0;
                }
                catch
                {
                    //try to convert
                    sbyte[] sb = Val as sbyte[];
                    int[] Tint = Val as int[];
                    double[] Tdoub = Val as double[];

                    if (sb != null)
                    {
                        tChannel.Put(sb);
                    }
                    else if (Tint != null)
                    {
                        tChannel.Put(Tint);
                    }
                    else if (Tdoub != null)
                    {
                        tChannel.Put(Tdoub);
                    }
                    else
                    {
                        throw new SystemException("type wrong, or maybe connection error time out");
                    }
                    return 0;
                }
            }
            
        }


        private void setVal(object Val,EpicsChannel tChannel)
        {
            IAsyncResult ASResult = setValDelegate.BeginInvoke(Val, tChannel, null, null);
            if (ASResult.AsyncWaitHandle.WaitOne(3000, false) == true)
            {
                setValDelegate.EndInvoke(ASResult);
                return;
            }
            else
            {
                throw new SystemException("connection time out.");
            }
            //return null;
        }




    }




    /// <summary>
    /// 
    /// </summary>
    public class ChannelSaver
    {
        public string PVName;
        public Type myType;
        public EpicsDelegate MonitorHandler;
        public ChannelSaver()
        {
            myType = null;
        }
    }
}
